package com.yanyu.space.common.gateway;

import com.alibaba.cloud.sentinel.gateway.ConfigConstants;
import com.yanyu.space.common.core.constants.SpaceConstants;
import com.yanyu.space.common.core.yaml.YamlPropertySourceFactory;
import com.yanyu.space.common.gateway.filter.LogFilter;
import com.yanyu.space.common.gateway.filter.support.DefaultLogPrint;
import com.yanyu.space.common.gateway.filter.support.ILogPrint;
import com.yanyu.space.common.gateway.filter.support.SleuthLogPrint;
import com.yanyu.space.common.gateway.handler.GlobalExceptionHandler;
import com.yanyu.space.common.gateway.handler.SentinelGatewayExceptionHandlerBean;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.web.ServerProperties;
import org.springframework.boot.autoconfigure.web.WebProperties;
import org.springframework.boot.web.reactive.error.ErrorAttributes;
import org.springframework.boot.web.reactive.error.ErrorWebExceptionHandler;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.annotation.Order;
import org.springframework.http.codec.ServerCodecConfigurer;
import org.springframework.web.reactive.result.view.ViewResolver;

import java.util.Collections;
import java.util.List;

/**
 * @author gz
 */
@Configuration
@Slf4j
@PropertySource(factory = YamlPropertySourceFactory.class, value = "classpath:" + SpaceConstants.Properties.GATEWAY_PROP)
public class SpaceGatewayAutoConfig {

    @Bean
    @ConditionalOnClass(name = "org.springframework.cloud.sleuth.Tracer")
    public SleuthLogPrint sleuthLogPrint() {
        return new SleuthLogPrint();
    }

    @Bean
    public LogFilter logFilter(ObjectProvider<ILogPrint> abstraceLogPrintObjectProvider) {
        ILogPrint iLogPrint = abstraceLogPrintObjectProvider.getIfAvailable(() -> new DefaultLogPrint());
        return new LogFilter(iLogPrint);
    }

    @Configuration
    @ConditionalOnProperty(prefix = ConfigConstants.GATEWAY_PREFIX, name = "enabled",
            havingValue = "true", matchIfMissing = true)
    @Import(SentinelGatewayExceptionHandlerBean.class)
    public class SentinelGatewayExceptionHandlerConfig {
    }

    @Configuration
    public class GlobalExceptionConfig {

        private final ServerProperties serverProperties;
        private final ApplicationContext applicationContext;
        private final WebProperties.Resources resources;
        private final List<ViewResolver> viewResolvers;
        private final ServerCodecConfigurer serverCodecConfigurer;

        public GlobalExceptionConfig(ServerProperties serverProperties,
                                     WebProperties webProperties,
                                     ObjectProvider<List<ViewResolver>> viewResolversProvider,
                                     ServerCodecConfigurer serverCodecConfigurer,
                                     ApplicationContext applicationContext) {
            this.serverProperties = serverProperties;
            this.applicationContext = applicationContext;
            this.resources = webProperties.getResources();
            this.viewResolvers = viewResolversProvider.getIfAvailable(Collections::emptyList);
            this.serverCodecConfigurer = serverCodecConfigurer;
        }

        @Bean("spaceErrorWebExceptionHandler")
        @Order(-1)
        public ErrorWebExceptionHandler errorWebExceptionHandler(ErrorAttributes errorAttributes) {
            GlobalExceptionHandler globalExceptionHandler = new GlobalExceptionHandler(
                    errorAttributes,
                    this.resources,
                    this.serverProperties.getError(),
                    this.applicationContext);
            globalExceptionHandler.setViewResolvers(this.viewResolvers);
            globalExceptionHandler.setMessageWriters(this.serverCodecConfigurer.getWriters());
            globalExceptionHandler.setMessageReaders(this.serverCodecConfigurer.getReaders());
            return globalExceptionHandler;
        }
    }
}
